package javasec.contempt.data;


/* Seed Connection
 * 
 * This class manages a single connection to a seed server
 * 
 * When contempt starts, it gets a list of known seeds from a local database.
 * This 
 * 
 * init()
 * 
 * connect() // connect to a this seed server
 * disconnect() // disconnect from this seed server
 * 
 * connectNew(hostname) // connect to an unknown seed server
 * connectDifferent(int,hostname) // connect to a known server at a new location
 *
 */



import java.io.File;
import java.io.InputStream;

import java.io.FileInputStream;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMISocketFactory;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javasec.contempt.Activator;
import javasec.contempt.actions.StartSeedAction;
import javasec.contempt.consoles.SeedLogConsole;
import javasec.contempt.consoles.SeedShellConsole;
import javasec.contempt.data.SeedNotifier;
import javasec.contempt.dialogs.StartSeedDialog;
import javasec.contempt.views.ConnectionsView.SeedServerNode;
import javasec.seed.DataSeedInterface;
import javasec.seed.ManagerSeedInterface;
import javasec.seed.SeedInterface;
import javasec.seed.SeedNotification;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.rmi.ssl.SslRMIClientSocketFactory;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tray;
import org.eclipse.swt.widgets.TrayItem;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.plugin.AbstractUIPlugin;



import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.KeyStore;

import sun.security.x509.X500Name;
import sun.security.x509.CertAndKeyGen;
import java.util.Date;
import java.io.FileOutputStream;

public class SeedConnection
{
	
	public Display display=Display.getCurrent();
	
	// variable to be set when the connection is loaded
	public String name;
	public String location;
	public Registry rmiRegistry=null;
	
	public ManagerSeedInterface managerSeed=null;
	public DataSeedInterface dataSeed=null;
	
	public boolean connected=false;
	public SeedNotifier notifier=null;

	public Thread notificationThread;
	public String notificationID;

	public SeedServerNode widget;
	
	public SeedConnection(String newName, String newLocation)
	{
		name=newName;
		location=newLocation;
		connected=false;
		
		try
		{	
			notifier=new SeedNotifier();
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		
		notificationThread=new Thread()
		{
			public void run()
			{
				// generate a random ID to retrieve notifications
				notificationID=new SecureRandom().nextInt()+"";
				
				while(true)
				{
					try
					{
						if(connected==true)
						{
							Thread.sleep(1000);
							ArrayList<SeedNotification> notifications=null;
							
							// get the notifications from the seed server
							notifications=managerSeed.getNotifications(notificationID);
							//System.out.println(name+" ding!");
							
							// distribute the notifcations to the UI
							for(SeedNotification n:notifications)
								notifier.notifyClient(n.key,n.data);
						}
						else
						{
							Thread.sleep(1000);
							//System.out.println(name+" dong!");
						}
					}
					catch(Exception e)
					{
						// disconnect when heartbeat is lost
						// this needs to be run in the UI thread
						Display.getDefault().syncExec(new Runnable()
						{
							public void run()
							{
								disconnect();
							}
						});
						
					}
				}
			}
		};
		notificationThread.start();		
	}
	
	public boolean disconnect()
	{
		// make sure contempt is already connected
		if(connected==false)return false;
		
		rmiRegistry=null;
		
		connected=false;
		if(widget!=null && widget.connected==true)widget.disconnect();
		
		return true;
	}

	public boolean connect()
	{
		if(connected==true)return true;
				
		SslRMIClientSocketFactory csf=new SslRMIClientSocketFactory();

		try
		{
			rmiRegistry=LocateRegistry.getRegistry(location,41414,csf);
			managerSeed=(ManagerSeedInterface)rmiRegistry.lookup("ManagerSeed");
			dataSeed=(DataSeedInterface)rmiRegistry.lookup("DataSeed");	
		}
		catch(Exception e)
		{
			if(location.equals("127.0.0.1")) // could not connect to localhost
			{
				// give the user a chance to launch a seed on localhost
				try
				{
					StartSeedDialog dialog=new StartSeedDialog(null);
					dialog.open();
					if(dialog.aborted)return false;
							
					// these seem not to be set properly
					rmiRegistry=LocateRegistry.getRegistry(location,41414,csf);				
					managerSeed=(ManagerSeedInterface)rmiRegistry.lookup("ManagerSeed");
					dataSeed=(DataSeedInterface)rmiRegistry.lookup("DataSeed");
				}
				catch(Exception e2)
				{
					MessageDialog.openError(
							SeedConnector.window.getShell(),
							"connection error",
							"could not connect to local seed"+"\n"+e2);
					
					e.printStackTrace();
					connected=false;
					return false;
				}
			}
			else // could not connect to remote host
			{
				MessageDialog.openError(
					SeedConnector.window.getShell(),
					"connection error",
					"could not connect to "+location+"\n"+e);
				e.printStackTrace();
				connected=false;
				return false;
			}
		}
			
		connected=true;
		
		// make sure the widget gets connected
		if(widget!=null&& widget.connected==false)widget.connect();
		
		return true;
	}

	public boolean connectNew(String server)
	{
		if(connected==true)this.disconnect();

		SslRMIClientSocketFactory csf=new SslRMIClientSocketFactory();

		try
		{
			rmiRegistry=LocateRegistry.getRegistry(server,41414,csf);
		}
		catch (RemoteException e)
		{
			e.printStackTrace();
			connected=false;
			return false;
		}
		
		try
		{
			managerSeed=(ManagerSeedInterface)rmiRegistry.lookup("ManagerSeed");
			dataSeed=(DataSeedInterface)rmiRegistry.lookup("DataSeed");
			
			//managerSeed.addNotifier(notifier);
		}
		catch(Exception e)
		{
			MessageDialog.openError(
				SeedConnector.window.getShell(),
				"connection error",
				"could not connect to "+server);			
				
			e.printStackTrace();
			connected=false;
			return false;
		}
		
		connected=true;
		return true;
	}
	
	public void newShell(Object data)
	{
		SeedShellConsole console;
		IConsoleManager consoleManager=ConsolePlugin.getDefault().getConsoleManager();
		
		Integer id=new Integer(data.toString());
		
		System.out.println("creating a new shell");
		
		console=new SeedShellConsole("shell."+id,null,id);
		//console.activate();
		consoleManager.addConsoles(new IConsole[]{console});
		consoleManager.addConsoles(consoleManager.getConsoles());
	}
		
	public void removeShell(Object data)
	{
		//SeedShellConsole console;
		IConsoleManager consoleManager=ConsolePlugin.getDefault().getConsoleManager();
		
		Integer id=new Integer(data.toString());
		
		System.out.println("removing a shell");
		
		for(IConsole console : consoleManager.getConsoles())
		{
			if(console.getName().equals("shell."+id))
			{
				consoleManager.removeConsoles(new IConsole[]{console});
			}
		}
	}	
	
}
